﻿using Hangfire;
using Hangfire.Common;
using Hangfire.Dashboard;
using Hangfire.States;
using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Xml.Linq;
using static Hangfire.Storage.JobStorageFeatures;
namespace HangfireExtensions
{
    public class HangfireAPIService
    {
        IBackgroundJobClient client;

        public HangfireAPIService(IBackgroundJobClient client)
        {
            this.client = client;
        }


        public IResultDTO<PageOutput<object>> GetScheduledJobs(PageInput req)
        {
            var total = JobStorage.Current.GetMonitoringApi().ScheduledCount();
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;
            var jobs = JobStorage.Current.GetMonitoringApi().ScheduledJobs(min, max);
            List<object> list1 = new List<object>();
            foreach (var job in jobs)
            {
                list1.Add(new
                {
                    key = job.Key,
                    //  job.Value.Job.Args,
                    Queue = job.Value.Job != null ? job.Value.Job.Queue : "",
                    Name = job.Value.Job != null ? job.Value.Job.Method.Name : "",
                    FullName = job.Value.Job != null ? job.Value.Job.Type.FullName : "",
                    Type = job.Value.InvocationData != null ? job.Value.InvocationData.Type : "",
                    Arguments = job.Value.InvocationData != null ? job.Value.InvocationData.Arguments : "",
                    ParameterTypes = job.Value.InvocationData != null ? job.Value.InvocationData.ParameterTypes : "",
                    //  job.Value.StateData,
                    EnqueueAt = job.Value.EnqueueAt.ToLocalTime(),
                    job.Value.InScheduledState,
                    LoadException = job.Value.LoadException?.Message,
                    ScheduledAt = job.Value.ScheduledAt.ToLoal(),
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                PageSize = req.PageSize,
                CurrentPage = req.CurrentPage,
                Total = total,
                List = list1
            });
        }


        public IResultDTO<PageOutput<object>> GetEnqueuedJobs(PagePara<string> req)
        {
            var total = JobStorage.Current.GetMonitoringApi().EnqueuedCount(req.Filter);
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().EnqueuedJobs(req.Filter, min, max);
            List<object> list = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    job.Value.InEnqueuedState,
                    EnqueuedAt = job.Value.EnqueuedAt.ToLoal(),
                    job.Value.State,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }


        public IResultDTO<PageOutput<object>> GetFetchedJobs(PagePara<string> req)
        {
            var total = JobStorage.Current.GetMonitoringApi().FetchedCount(req.Filter);
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().FetchedJobs(req.Filter, min, max);
            List<object> list = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    FetchedAt = job.Value.FetchedAt.ToLoal(),
                    job.Value.State,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }


        public IResultDTO<PageOutput<object>> GetFailedJobs(PageInput req)
        {
            var total = JobStorage.Current.GetMonitoringApi().FailedCount();
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().FailedJobs(min, max);
            List<object> list = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    FailedAt = job.Value.FailedAt.ToLoal(),
                    job.Value.StateData,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }


        public IResultDTO<PageOutput<object>> GetProcessingJobs(PageInput req)
        {
            var total = JobStorage.Current.GetMonitoringApi().ProcessingCount();
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().ProcessingJobs(min, max);
            List<object> list = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    StartedAt = job.Value.StartedAt.ToLoal(),
                    job.Value.InProcessingState,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }


        public IResultDTO<PageOutput<object>> GetSucceededJobs(PageInput req)
        {
            var total = JobStorage.Current.GetMonitoringApi().SucceededListCount();
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().SucceededJobs(min, max);
            List<object> list
                = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    job.Value.TotalDuration,
                    SucceededAt = job.Value.SucceededAt.ToLoal(),
                    job.Value.InSucceededState,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                    job.Value.Result
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }


        public IResultDTO<PageOutput<object>> GetDeletedJobs(PageInput req)
        {
            var total = JobStorage.Current.GetMonitoringApi().DeletedListCount();
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
            if (max > total) max = (int)total;

            var jobs = JobStorage.Current.GetMonitoringApi().DeletedJobs(min, max);
            List<object> list = new List<object>();
            foreach (var job in jobs)
            {
                list.Add(new
                {
                    key = job.Key,
                    DeletedAt = job.Value.DeletedAt.ToLoal(),
                    job.Value.StateData,
                    job.Value.Job.Type.FullName,
                    job.Value.Job.Method.Name,
                });
            }
            return ResultDTO.Ok(new PageOutput<object>()
            {
                CurrentPage = req.CurrentPage,
                PageSize = req.PageSize,
                Total = total,
                List = list
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        /// <exception cref="NotSupportedException"></exception>
        public IResultDTO<PageOutput<object>> GetAwaitingJobs(PageInput req)
        {
            var min = req.CurrentPage - 1;
            var max = (req.CurrentPage - 1) + req.PageSize;
          


            if (JobStorage.Current.HasFeature(JobStorageFeatures.Monitoring.AwaitingJobs))
            {
                var monitor = JobStorage.Current.GetMonitoringApi() as JobStorageMonitor;
                if (monitor == null)
                {
                    throw new NotSupportedException("MonitoringApi should inherit the `JobStorageMonitor` class");
                }
                var total = monitor.AwaitingCount();
                if (max > total) max = (int)total;
                var jobs = monitor.AwaitingJobs(min, max);
                List<object> list = new List<object>();
                foreach (var job in jobs)
                {
                    list.Add(new
                    {
                        key = job.Key,
                        AwaitingAt = job.Value.AwaitingAt.ToLoal(),
                        job.Value.StateData,
                        job.Value.Job.Type.FullName,
                        job.Value.Job.Method.Name,
                    });
                }
                return ResultDTO.Ok(new PageOutput<object>()
                {
                    CurrentPage = req.CurrentPage,
                    PageSize = req.PageSize,
                    Total = total,
                    List = list
                });
 
            }
            else
            {
                using (var connection = JobStorage.Current.GetReadOnlyConnection())
                {
                    var storageConnection = connection as JobStorageConnection;

                    if (storageConnection != null)
                    {
                        var total = storageConnection.GetSetCount("awaiting");
                        if (max > total) max = (int)total;
                        var jobs = storageConnection.GetRangeFromSet("awaiting", min, max);
                        List<object> list = new List<object>();
                        foreach (var job in jobs)
                        {
                            list.Add(new
                            {
                                key = job,
                            });
                        }
                        return ResultDTO.Ok(new PageOutput<object>()
                        {
                            CurrentPage = req.CurrentPage,
                            PageSize = req.PageSize,
                            Total = total,
                            List = list
                        });
                    }
                }
            }
            return ResultDTO.Ok(new PageOutput<object>());
        }


        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IResultDTO<IList<QueueWithTopEnqueuedJobsDto>> Queues()
        {
            return ResultDTO.Ok(JobStorage.Current.GetMonitoringApi().Queues());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IResultDTO GetRecurringJobs()
        {
            using (var connection = JobStorage.Current.GetConnection())
            {
                var joblist = StorageConnectionExtensions.GetRecurringJobs(connection);

                return ResultDTO.Ok(joblist.Select(x => new
                {

                    x.LastJobId,
                    x.Id,
                    x.Job.Type.FullName,
                    x.Job.Queue,
                    x.Job.Args,
                    x.Job.Method.Name,
                    CreatedAt = x.CreatedAt.ToLoal(),
                    x.Cron,
                    x.Removed,
                    x.RetryAttempt,
                    x.TimeZoneId,
                    NextExecution = x.NextExecution.ToLoal(),
                    x.LastJobState,
                    LastExecution = x.LastExecution.ToLoal(),
                    LoadException = x.LoadException != null ? new
                    {
                        x.LoadException.Message,
                        LoadException = x.LoadException.ToString()
                    } : null,
                }));
            }
        }




        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IResultDTO<IList<ServerDto>> Servers()
        {
            return ResultDTO.Ok(JobStorage.Current.GetMonitoringApi().Servers());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public IResultDTO<object> JobDetails(string jobId)
        {
            var detail = JobStorage.Current.GetMonitoringApi().JobDetails(jobId);

            object r = new
            {
                ExpireAt = detail.ExpireAt.ToLoal(),
                detail.Job.Queue,
                detail.Job.Method.Name,
                detail.Job.Type.FullName,
                detail.Job.Args,
                CreatedAt = detail.CreatedAt.ToLoal(),
                detail.Properties,
                LoadException = detail.LoadException == null ? null : new
                {
                    Message = detail.LoadException?.Message ?? "",
                    LoadException = detail.LoadException?.ToString(),
                },
                History = detail.History.Select(x => new
                {
                    x.Reason,
                    x.StateName,
                    x.Data,
                    CreatedAt = x.CreatedAt.ToLocalTime()
                })
            };
            return ResultDTO.Ok(r);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IResultDTO<StatisticsDto> GetStatistics()
        {
            return ResultDTO.Ok(JobStorage.Current.GetMonitoringApi().GetStatistics());
        }



        /// <summary>
        /// 手动触发一个周期任务
        /// </summary>
        /// <param name="JobName"></param>
        /// <returns></returns>
        public IResultDTO<string> TrrigerJob(string JobName)
        {
            return ResultDTO.Ok<string>(RecurringJob.TriggerJob(JobName));
        }

        /// <summary>
        /// 手动触发一个周期任务
        /// </summary>
        /// <param name="JobName"></param>
        /// <returns></returns>
        public IResultDTO<string> TrrigerJobs(string[] JobNames)
        {
            var s = "";
            foreach (var item in JobNames)
            {
               s+= RecurringJob.TriggerJob(item);
            }
            return ResultDTO.Ok<string>(s);
        }
        /// <summary>
        /// 删除一个周期任务
        /// </summary>
        /// <param name="jobname"></param>
        /// <returns></returns>
        public IResultDTO DeleteRecurringJob(string jobname)
        {
            RecurringJob.RemoveIfExists(jobname);
            return ResultDTO.Ok();
        }
        /// <summary>
        /// 删除一个周期任务
        /// </summary>
        /// <param name="jobname"></param>
        /// <returns></returns>
        public IResultDTO DeleteRecurringJobs(string[] jobname)
        {
            foreach (var item in jobname)
            {
                RecurringJob.RemoveIfExists(item);
            }
            return ResultDTO.Ok();
        }
        /// <summary>
        /// 删除一个延时任务
        /// </summary>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public IResultDTO DeleteBackgroundJob(string jobId)
        {
            // client.ChangeState(jobId, new DeletedState { Reason = "Triggered via Dashboard UI" });

            // new EnqueuedState { Reason = "Triggered via Dashboard UI" }
            return ResultDTO.Result(BackgroundJob.Delete(jobId));
        }

        public IResultDTO DeleteBackgroundJobs(string[] jobIds)
        {
            // client.ChangeState(jobId, new DeletedState { Reason = "Triggered via Dashboard UI" });

            // new EnqueuedState { Reason = "Triggered via Dashboard UI" }

            foreach (var item in jobIds)
            {
                if (!BackgroundJob.Delete(item))
                    return ResultDTO.Error($"删除{item}失败");
            }
            return ResultDTO.Ok();
        }
        /// <summary>
        /// 重新加一个延时任务
        /// </summary>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public IResultDTO Reschedule(string jobId, DateTime enqueueAt)
        {
            return ResultDTO.Result(BackgroundJob.Reschedule(jobId, enqueueAt));
        }
        public IResultDTO Reschedules(string[] jobIds, DateTime enqueueAt)
        {
            foreach (var item in jobIds)
            {
                if (!BackgroundJob.Reschedule(item, enqueueAt))
                {
                    return ResultDTO.Error($"{item}加入失败");
                }
            }
            return ResultDTO.Ok();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public IResultDTO ReRequeue(string jobId)
        {
            return ResultDTO.Result(BackgroundJob.Requeue(jobId));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jobIds"></param>
        /// <returns></returns>
        public IResultDTO ReRequeue(string[] jobIds)
        {
            Dictionary<string, bool> res = new Dictionary<string, bool>();
            foreach (var item in jobIds)
            {
                var r = BackgroundJob.Requeue(item);
                res.Add(item, r);
            }
            return ResultDTO.Ok(res);
        }


     

        /*  /// <summary>
          /// 获取所有周期任务
          /// </summary>
          /// <returns></returns>
          public async Task<List<HttpJobItem>> GetAllRecurringHttpJobs()
          {
              var list = new List<HttpJobItem>();
              using (var connection = JobStorage.Current.GetConnection())
              {
                  var joblist = StorageConnectionExtensions.GetRecurringJobs(connection);
                  joblist.ForEach(job =>
                  {
                      if (job.Job.Args.FirstOrDefault() != null && job.Job.Args.FirstOrDefault()?.ToString() != "0")
                      {
                          var httpjob = JsonConvert.DeserializeObject<HttpJobItem>(job.Job.Args.FirstOrDefault()?.ToString());
                          if (httpjob != null) list.Add(httpjob);
                      }

                  });
              }
              return await Task.FromResult(list);
          }


          public async Task<List<HttpJobItem>> GetAllRecurringHttpJob(string jobName)
          {
              var list = new List<HttpJobItem>();
              using (var connection = JobStorage.Current.GetConnection())
              {
                  var joblist = StorageConnectionExtensions.GetRecurringJobs(connection);
                  joblist.ForEach(job =>
                  {
                      if (job.Job.Args.FirstOrDefault() != null && job.Job.Args.FirstOrDefault()?.ToString() != "0")
                      {
                          var httpjob = JsonConvert.DeserializeObject<HttpJobItem>(job.Job.Args.FirstOrDefault()?.ToString());
                          if (httpjob?.JobName == jobName)
                          {
                              list.Add(httpjob);
                          }
                      }
                  });
              }
              return await Task.FromResult(list);
          }
  */
    }
}
